home *** CD-ROM | disk | FTP | other *** search
- //
- // RegularPolyhedraView - a flexible, bouncing polyhedron.
- //
- // Module for BackSpace.app
- // 22 Nov 91 - 8 Dec 91.
- // Simon Marchant, and Paul Brown (simon@math.berkeley.edu,
- // pbrown@math.berkeley.edu).
- //
-
- #import "PolyhedraView.h"
- #import "PolyhedraViewWraps.h"
-
- #import <appkit/graphics.h>
- #import <appkit/Matrix.h>
- #import <libc.h>
- #import <dpsclient/wraps.h>
- #import <math.h>
- #import <appkit/publicWraps.h>
-
- // Number of vertices of the polyhedron
- static int theNumVertices[NUM_POLYHEDRA] =
- {4, 8, 6, 20, 12};
-
- // Number of vertices adjacent to a vertex.
- static int theNumAdjacents[NUM_POLYHEDRA] =
- {3, 3, 4, 3, 5};
-
- // Number of faces of the polyhedron.
- static int theNumFaces[NUM_POLYHEDRA] =
- {4, 6, 8, 12, 20};
-
- // Number of vertices on each face.
- static int theVerticesPerFace[NUM_POLYHEDRA] =
- {3, 4, 3, 5, 3};
-
- // Number of non NO_DRAW faces - i.e. faces that we actually bother drawing.
- static int realFaces[NUM_POLYHEDRA] =
- {3, 4, 4, 9, 14};
-
- // Numbers describing the 3D co-ordinates of the polyhedra - the initial positions.
- static D3_PT offsets[NUM_POLYHEDRA][MAX_NUM_VERTICES] = {
- {{ 0, 0, 1.73205}, // Tetrahedron
- { 0, 1.63299, -0.57735},
- {-1.41421, -0.816497, -0.57735},
- { 1.41421, -0.816497, -0.57735},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0}},
- {{ 0.707107, 0.707107, 0.707107}, // Cube.
- {-0.707107, 0.707107, 0.707107},
- {-0.707107, -0.707107, 0.707107},
- { 0.707107, -0.707107, 0.707107},
- {-0.707107, -0.707107, -0.707107},
- { 0.707107, -0.707107, -0.707107},
- { 0.707107, 0.707107, -0.707107},
- {-0.707107, 0.707107, -0.707107},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0}},
- {{ 0, 0, 1.41421}, // Octahedron
- { 1.41421, 0, 0},
- { 0, 1.41421, 0},
- { 0, 0, -1.41421},
- {-1.41421, 0, 0},
- { 0, -1.41421, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0}},
- {{ 0.525731, 0.381966, 0.850651}, //Dodecahedron.
- {-0.200811, 0.618034, 0.850651},
- {-0.649839, 0, 0.850651},
- {-0.200811, -0.618034, 0.850651},
- { 0.525731, -0.381966, 0.850651},
- { 0.850651, 0.618034, 0.200811},
- {-0.32492, 1.0, 0.200811},
- {-1.05146, 0, 0.200811},
- {-0.32492, -1.0, 0.200811},
- { 0.850651, -0.618034, 0.200811},
- { 0.32492, 1.0, -0.200811},
- {-0.850651, 0.618034, -0.200811},
- {-0.850651, -0.618034, -0.200811},
- { 0.32492, -1.0, -0.200811},
- { 1.05146, 0, -0.200811},
- { 0.200811, 0.618034, -0.850651},
- {-0.525731, 0.381966, -0.850651},
- {-0.525731, -0.381966, -0.850651},
- { 0.200811, -0.618034, -0.850651},
- { 0.649839, 0, -0.850651}},
- {{ 0, 0, 1.0}, // Icosahedron.
- { 0.894427, 0, 0.447214},
- { 0.276393, 0.850651, 0.447214},
- {-0.723607, 0.525731, 0.447214},
- {-0.723607, -0.525731, 0.447214},
- { 0.276393, -0.850651, 0.447214},
- { 0.723607, 0.525731, -0.447214},
- {-0.276393, 0.850651, -0.447214},
- {-0.894427, 0, -0.447214},
- {-0.276393, -0.850651, -0.447214},
- { 0.723607, -0.525731, -0.447214},
- { 0, 0, -1.0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0},
- { 0, 0, 0}}};
-
- // List of faces in the polyhedron.
- static int faces[NUM_POLYHEDRA][MAX_NUM_FACES][MAX_VERTICES_PER_FACE] = {
- {{0, 1, 2, -1, -1}, // Tetrahedron.
- {0, 2, 3, -1, -1},
- {0, 3, 1, -1, -1},
- {1, 3, 2, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1}},
- {{0, 1, 2, 3, -1}, // Cube.
- {0, 3, 5, 6, -1},
- {0, 6, 7, 1, -1},
- {1, 7, 4, 2, -1},
- {4, 7, 6, 5, -1},
- {2, 4, 5, 3, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1}},
- {{0, 1, 2, -1, -1}, // Octahedron.
- {0, 2, 4, -1, -1},
- {0, 4, 5, -1, -1},
- {0, 5, 1, -1, -1},
- {1, 5, 3, -1, -1},
- {1, 3, 2, -1, -1},
- {3, 5, 4, -1, -1},
- {2, 3, 4, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1}},
- {{0, 1, 2, 3, 4}, // Dodecahedron.
- {0, 4, 9, 14, 5},
- {0, 5, 10, 6, 1},
- {1, 6, 11, 7, 2},
- {2, 7, 12, 8, 3},
- {3, 4, 9, 13, 8},
- {5, 14, 19, 15, 10},
- {6, 10, 15, 16, 11},
- {7, 11, 16, 17, 12},
- {8, 12, 17, 18, 13},
- {9, 13, 18, 19, 14},
- {15, 19, 18, 17, 16},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1},
- {-1, -1, -1, -1, -1}},
- {{0, 2, 1, -1, -1}, // Icosahedron.
- {0, 3, 2, -1, -1},
- {0, 4, 3, -1, -1},
- {0, 5, 4, -1, -1},
- {0, 1, 5, -1, -1},
- {1, 2, 6, -1, -1},
- {2, 3, 7, -1, -1},
- {3, 4, 8, -1, -1},
- {4, 5, 9, -1, -1},
- {5, 1, 10, -1, -1},
- {6, 2, 7, -1, -1},
- {7, 3, 8, -1, -1},
- {8, 4, 9, -1, -1},
- {9, 5, 10, -1, -1},
- {10, 1, 6, -1, -1},
- {6, 7, 11, -1, -1},
- {7, 8, 11, -1, -1},
- {8, 9, 11, -1, -1},
- {9, 10, 11, -1, -1},
- {10, 6, 11, -1, -1}}};
-
- // Array following contains the vertex adjacency information, so we can
- // determine which n vertices are adjacent to any given one.
-
-
- typedef struct { float r,g,b; } rgbcolor;
-
- static rgbcolor clut[5] = {
- {1,0,0},
- {0,1,.2},
- {0,0,1},
- {1,.8,0},
- {1,.3,0}
- };
-
- // Two "pseudo-colours".
- #define TRANSPARENT -1
- #define NO_DRAW -2
-
- // Colour that you draw the face with.
- // TRANSPARENT means just draw the edges.
- // NO_DRAW means that the face is transparent, and other faces
- // draw all the edges of the face - so no need to draw it.
- static int faceColour[NUM_POLYHEDRA][MAX_NUM_FACES] =
- {{0, TRANSPARENT, TRANSPARENT, NO_DRAW,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT},
- {1, NO_DRAW, TRANSPARENT, NO_DRAW,
- 2, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT},
- {3, NO_DRAW, 4, NO_DRAW,
- 0, NO_DRAW, NO_DRAW, 1,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT},
- {2, NO_DRAW, TRANSPARENT, NO_DRAW,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, 3,
- TRANSPARENT, NO_DRAW, 4, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, TRANSPARENT, TRANSPARENT, TRANSPARENT},
- {0, TRANSPARENT, 1, NO_DRAW, TRANSPARENT,
- NO_DRAW, NO_DRAW, NO_DRAW, 2, 3,
- 4, 0, TRANSPARENT, TRANSPARENT, TRANSPARENT,
- TRANSPARENT, NO_DRAW, 1, NO_DRAW, 2}};
-
- @implementation PolyhedraView
-
- // Distance between two points. Inlined for efficiency.
- inline float distance(NXCoord xcrd, NXCoord ycrd, NXCoord zcrd);
-
- inline float distance(NXCoord xcrd, NXCoord ycrd, NXCoord zcrd)
- {
- return sqrt(xcrd * xcrd + ycrd * ycrd + zcrd * zcrd);
- }
-
- // Draw a line in the proper perspectice projection from pt1 to pt2
- - perspectiveLineFrom:(D3_PT)pt1 to:(D3_PT)pt2
- {
- if (perspectivePt.z == 0)
- return self;
- PSmoveto(pt1.x - pt1.z * (pt1.x - perspectivePt.x) / perspectivePt.z,
- pt1.y - pt1.z * (pt1.y - perspectivePt.y) / perspectivePt.z);
- PSlineto(pt2.x - pt2.z * (pt2.x - perspectivePt.x) / perspectivePt.z,
- pt2.y - pt2.z * (pt2.y - perspectivePt.y) / perspectivePt.z);
- return self;
- }
-
- // Draw the background room - in the appropriate gray.
- - drawBoxInColour:(float)theGray
- {
- return self; //by sam, I don't like the room!
-
- #ifdef VANNA
- D3_PT pt1, pt2;
- PSsetgray (theGray);
- pt1.x = 0;
- pt1.y = 0;
- pt1.z = 0;
- pt2.x = 0;
- pt2.y = 0;
- pt2.z = backTopRight.z;
- [self perspectiveLineFrom:pt1 to:pt2];
- pt1.z = backTopRight.z;
- pt1.y = backTopRight.y;
- [self perspectiveLineFrom:pt2 to:pt1];
- pt2.z = 0;
- pt2.y = backTopRight.y;
- [self perspectiveLineFrom:pt1 to:pt2];
- pt2 = backTopRight;
- [self perspectiveLineFrom:pt1 to:pt2];
- pt1.x = backTopRight.x;
- pt1.z = 0;
- [self perspectiveLineFrom:pt2 to:pt1];
- pt1.y = 0;
- pt1.z = backTopRight.z;
- [self perspectiveLineFrom:pt2 to:pt1];
- pt2.y = 0;
- pt2.z = 0;
- [self perspectiveLineFrom:pt1 to:pt2];
- pt2.z = backTopRight.z;
- pt2.x = 0;
- [self perspectiveLineFrom:pt1 to:pt2];
- PSstroke();
- return self;
- #endif
- }
-
- // Draw the polyhedron at the current position.
- - drawPolyhedron
- {
- int i, j, k, m, n=0;
- float faceVerticesZ[MAX_NUM_FACES][MAX_VERTICES_PER_FACE];
- float sortedVerticesZ[MAX_NUM_FACES][MAX_VERTICES_PER_FACE];
- NXPoint faceVerticesScreen[MAX_NUM_FACES][MAX_VERTICES_PER_FACE];
- BOOL drawn[MAX_NUM_FACES];
- BOOL intersect;
- NXPoint *thisFace, *tempFace, *firstVertex, *secondVertex, *thirdVertex, *fourthVertex;
- int colours[MAX_NUM_FACES];
- float r[MAX_NUM_FACES];
- float g[MAX_NUM_FACES];
- float b[MAX_NUM_FACES];
- VERTEX *thisVertex;
- float firstVertexZ, secondVertexZ, thirdVertexZ, fourthVertexZ;
- float det, s=0, t=0;
-
- // Pre-compute the positions of each of the vertices as they're drawn on the screen. We'll need
- // them a little later, and we'll keep them around until we erase this polyhedron from the screen.
- for (i = 0; i < numVertices; i++)
- {
- thisVertex = vertices + i;
- thisVertex->screenPos.x = perspectivePt.x + (thisVertex->pos.x - perspectivePt.x) * perspectivePt.z / (perspectivePt.z + thisVertex->pos.z);
- thisVertex->screenPos.y = perspectivePt.y + (thisVertex->pos.y - perspectivePt.y) * perspectivePt.z / (perspectivePt.z + thisVertex->pos.z);
- }
- // Pick out the faces we have to draw, and grab an ordered list of the z-coordinates of each
- // vertex in each face, for later on.
- k = 0;
- for (i = 0; i < numFaces; i++)
- if (faceColour[polyhedron][i] != NO_DRAW)
- {
- for (j = 0; j < verticesPerFace; j++)
- {
- thisVertex = &( vertices[faces[polyhedron][i][j]]);
- faceVerticesScreen[k][j] = thisVertex -> screenPos;
- faceVerticesZ[k][j] = thisVertex->pos.z;
- for (m = 0; (m < j) && (sortedVerticesZ[k][m] > thisVertex->pos.z); m++)
- ;
- for (n = j; n > m; n--)
- sortedVerticesZ[k][n] = sortedVerticesZ[k][n - 1];
- sortedVerticesZ[k][m] = thisVertex->pos.z;
- }
- colours[k] = faceColour[polyhedron][i];
- if (colours[k] != TRANSPARENT)
- {
- r[k] = clut[colours[k]].r;
- g[k] = clut[colours[k]].g;
- b[k] = clut[colours[k]].b;
- }
- drawn[k] = NO;
- k++;
- }
- // Now, run through the list of faces we have to draw, and select the next one to
- // draw - by making sure that it's not in front of any faces we haven't drawn
- // yet.
- for (i = 0; i < numDrawFaces; i++)
- {
- for (k = 0; drawn[k]; k++)
- ;
- thisFace = (NXPoint *)&(faceVerticesScreen[k]);
- for (j = k + 1; j < numDrawFaces; j++)
- if (!drawn[j])
- {
- tempFace = (NXPoint *)&(faceVerticesScreen[j]);
- intersect = NO;
- // check for edges intersecting.
- for (m = 0; (!intersect) && (m < verticesPerFace); m++)
- for (n = 0; (!intersect) && (n < verticesPerFace); n++)
- {
- firstVertex = thisFace + m;
- secondVertex = (m + 1 == verticesPerFace) ? thisFace : thisFace + m + 1;
- thirdVertex = tempFace + n;
- fourthVertex = (n + 1 == verticesPerFace) ? tempFace : tempFace + n + 1;
- if (((firstVertex->x != thirdVertex->x) || (firstVertex->y != thirdVertex->y)) &&
- ((firstVertex->x != fourthVertex->x) || (firstVertex->y != fourthVertex->y)) &&
- ((secondVertex->x != thirdVertex->x) || (secondVertex->y != thirdVertex->y)) &&
- ((secondVertex->x != fourthVertex->x) || (secondVertex->y != fourthVertex->y)))
- if ((det = ((firstVertex->x - secondVertex->x) * (fourthVertex->y - thirdVertex->y) -
- (firstVertex->y - secondVertex->y) * (fourthVertex->x - thirdVertex->x))) != 0)
- {
- t = ((fourthVertex->y - thirdVertex->y) * (fourthVertex->x - secondVertex->x) +
- (thirdVertex->x - fourthVertex->x) * (fourthVertex->y - secondVertex->y)) / det;
- s = ((secondVertex->y - firstVertex->y) * (fourthVertex->x - secondVertex->x) +
- (firstVertex->x - secondVertex->x) * (fourthVertex->y - secondVertex->y)) / det;
- if ((t > 0.0) && (t < 1.0) && (s > 0.0) & (s < 1.0))
- intersect = YES;
- }
- }
- m --;
- n --;
- // if no edges intersect, order by z-coordinates.
- if (!intersect)
- {
- for (m = 0; (m < verticesPerFace) && (sortedVerticesZ[k][m] == sortedVerticesZ[j][m]); m++)
- ;
- if ((m != verticesPerFace) && (sortedVerticesZ[j][m] > sortedVerticesZ[k][m]))
- {
- k = j;
- thisFace = tempFace;
- }
- // else
- // ;
- }
- else
- // if there's a pair of edges intersecting, look at the z-coord at the
- // intersection pt - the largest z-coord is the one we drawn.
- {
- firstVertexZ = faceVerticesZ[k][m];
- secondVertexZ = (m + 1 == verticesPerFace) ? faceVerticesZ[k][0] :
- faceVerticesZ[k][m + 1];
- thirdVertexZ = faceVerticesZ[j][n];
- fourthVertexZ = (n + 1 == verticesPerFace) ? faceVerticesZ[j][0] :
- faceVerticesZ[j][n + 1];
- if (firstVertexZ * t + secondVertexZ * (1 - t) < thirdVertexZ * s + fourthVertexZ * (1 - s))
- {
- k = j;
- thisFace = tempFace;
- }
- }
- }
-
-
-
- // Let the wraps do the drawing, depending on the number of vertices in
- // the face, and whether or not we should fill the face.
- if (verticesPerFace == 3)
- if (colours[k] != TRANSPARENT)
- colourTriangle(thisFace[0].x, thisFace[0].y, thisFace[1].x, thisFace[1].y,
- thisFace[2].x, thisFace[2].y, r[k],g[k],b[k]);
- else
- outlineTriangle(thisFace[0].x, thisFace[0].y, thisFace[1].x, thisFace[1].y,
- thisFace[2].x, thisFace[2].y);
- else
- if (verticesPerFace == 4)
- if (colours[k] != TRANSPARENT)
- colourSquare(thisFace[0].x, thisFace[0].y, thisFace[1].x, thisFace[1].y,
- thisFace[2].x, thisFace[2].y, thisFace[3].x, thisFace[3].y,
- r[k],g[k],b[k]);
- else
- outlineSquare(thisFace[0].x, thisFace[0].y, thisFace[1].x, thisFace[1].y,
- thisFace[2].x, thisFace[2].y, thisFace[3].x, thisFace[3].y);
- else
- if (verticesPerFace == 5)
- if (colours[k] != TRANSPARENT)
- colourPentagon(thisFace[0].x, thisFace[0].y, thisFace[1].x, thisFace[1].y,
- thisFace[2].x, thisFace[2].y, thisFace[3].x, thisFace[3].y,
- thisFace[4].x, thisFace[4].y, r[k],g[k],b[k]);
- else
- outlinePentagon(thisFace[0].x, thisFace[0].y, thisFace[1].x, thisFace[1].y,
- thisFace[2].x, thisFace[2].y, thisFace[3].x, thisFace[3].y,
- thisFace[4].x, thisFace[4].y);
-
- drawn[k] = YES;
- }
-
- return self;
- }
-
-
- // Erase the polyhedron. Quick, and dirty - just erase a large rectangle that
- // covers the entire polyhedron on the screen - if it erase some of the background, so
- // what? We'll redraw that in a little while, anyway. This method has the prime virtue of
- // being fast, fast, fast.
- - erasePolyhedron
- {
- int i;
- NXRect eraseRect;
- float maxX, maxY, minX, minY;
- NXPoint thisPt;
-
- maxY = maxX = 0;
- minX = frame.size.width;
- minY = frame.size.height;
- for (i = 0; i < numVertices; i++)
- {
- thisPt = vertices[i].screenPos;
- if (thisPt.x > maxX)
- maxX = thisPt.x;
- if (thisPt.y > maxY)
- maxY = thisPt.y;
- if (thisPt.x < minX)
- minX = thisPt.x;
- if (thisPt.y < minY)
- minY = thisPt.y;
- }
-
- eraseRect.origin.x = minX;
- eraseRect.origin.y = minY;
- eraseRect.size.width = maxX - minX + 1;
- eraseRect.size.height = maxY - minY + 1;
- PSsetgray(NX_BLACK);
- NXRectFill(&eraseRect);
- return self;
- }
-
- // Do one animation step.
- - oneStep
- {
- int i, j;
- float length;
- float dotProduct;
- D3_PT velForce[MAX_NUM_VERTICES], force[MAX_NUM_VERTICES];
- float theForce;
- D3_PT sumVel;
-
- // Cycle the background box colours.
- if (((backStep ++) % 20) == 0)
- {
- switch (backStep / 20)
- {
- #ifdef VANNA
- case 0:
- [self drawBoxInColour:NX_WHITE];
- break;
- case 1:
- [self drawBoxInColour:NX_LTGRAY];
- break;
- case 2:
- [self drawBoxInColour:NX_DKGRAY];
- break;
- case 3:
- [self drawBoxInColour:NX_BLACK];
- break;
- #endif
- default:
- // Compute average velocity of icosahedron
- // If it's too small, give it a random kick
- sumVel.x = sumVel.y = sumVel.z = 0;
- for (i = 0; i < numVertices; i++)
- {
- sumVel.x += vertices[i].vel.x;
- sumVel.y += vertices[i].vel.y;
- sumVel.z += vertices[i].vel.z;
- }
- if (distance(sumVel.x, sumVel.y, sumVel.z) / numVertices < 0.5) //.33
- {
- sumVel.x = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- sumVel.y = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- sumVel.z = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- for (i = 0; i < numVertices; i++)
- {
- vertices[i].vel.x += sumVel.x;
- vertices[i].vel.y += sumVel.y;
- vertices[i].vel.z += sumVel.z;
- }
- }
- backStep = 0;
- break;
- }
- }
- // If we're not doing anything about the polyhedron, leave now.
- if (noAnimation)
- return self;
- // Erase it.
- [self erasePolyhedron];
- // Move it, bouncing off walls as necessary
- for (i = 0; i < numVertices; i++)
- {
- vertices[i].pos.x += vertices[i].vel.x;
- if ((vertices[i].pos.x < 0) || (vertices[i].pos.x > backTopRight.x))
- vertices[i].vel.x = -vertices[i].vel.x;
- vertices[i].pos.y += vertices[i].vel.y;
- if ((vertices[i].pos.y < 0) || (vertices[i].pos.y > backTopRight.y))
- vertices[i].vel.y = -vertices[i].vel.y;
- vertices[i].pos.z += vertices[i].vel.z;
- if ((vertices[i].pos.z < 0) || (vertices[i].pos.z > backTopRight.z))
- vertices[i].vel.z = -vertices[i].vel.z;
- }
- // draw it
- [self drawPolyhedron];
- for (i = 0; i < numVertices; i++)
- {
- velForce[i].x = force[i].x = 0;
- velForce[i].y = force[i].y = 0;
- velForce[i].z = force[i].z = 0;
- }
- // calculate the force on each vertex.
- // Notice the use of symmetry here to cut down the amount of computation
- // i.e. the force on vertex j exerted by the spring from vertex i is minus
- // that on vertex i exerted by vertex j .....
- for (i = 0; i < numVertices; i++)
- {
- for (j = i + 1; j < numVertices; j++)
- {
- // spring forces (Hookes' Law - remember that?)
- length = distance(vertices[i].pos.x - vertices[j].pos.x,
- vertices[i].pos.y - vertices[j].pos.y,
- vertices[i].pos.z - vertices[j].pos.z);
- theForce = (vertices[i].pos.x - vertices[j].pos.x) / length * (restLengths[i][j] - length) * SPRING_K;
- force[i].x += theForce;
- force[j].x -= theForce;
- theForce = (vertices[i].pos.y - vertices[j].pos.y) / length * (restLengths[i][j] - length) * SPRING_K;
- force[i].y += theForce;
- force[j].y -= theForce;
- theForce = (vertices[i].pos.z - vertices[j].pos.z) / length * (restLengths[i][j] - length) * SPRING_K;
- force[i].z += theForce;
- force[j].z -= theForce;
-
- // Velocity damping - only for adjacent vertices
-
- if (isAdjacent[i][j])
- {
- dotProduct = ((vertices[i].pos.x - vertices[j].pos.x) * (vertices[i].vel.x - vertices[j].vel.x) +
- (vertices[i].pos.y - vertices[j].pos.y) * (vertices[i].vel.y - vertices[j].vel.y) +
- (vertices[i].pos.z - vertices[j].pos.z) * (vertices[i].vel.z - vertices[j].vel.z)) / length/length * damping;
-
- theForce = dotProduct * (vertices[i].pos.x - vertices[j].pos.x);
- velForce[i].x -= theForce;
- velForce[j].x += theForce;
- theForce = dotProduct * (vertices[i].pos.y - vertices[j].pos.y);
- velForce[i].y -= theForce;
- velForce[j].y += theForce;
- theForce = dotProduct * (vertices[i].pos.z - vertices[j].pos.z); //xxx
- velForce[i].z -= theForce;
- velForce[j].z += theForce;
- }
- }
- }
- // Change the velocities (F = ma !!). Make sure the velocities don't get too big.
- // (Stability check).
- for (i = 0; i < numVertices; i++)
- {
- vertices[i].vel.x += (velForce[i].x + force[i].x) / vertices[i].mass;
- if (fabs(vertices[i].vel.x) > MAX_VEL)
- vertices[i].vel.x = vertices[i].vel.x / fabs(vertices[i].vel.x) * MAX_VEL;
- vertices[i].vel.y += (velForce[i].y + force[i].y) / vertices[i].mass;
- if (fabs(vertices[i].vel.y) > MAX_VEL)
- vertices[i].vel.y = vertices[i].vel.y / fabs(vertices[i].vel.y) * MAX_VEL;
- vertices[i].vel.z += (velForce[i].z + force[i].z) / vertices[i].mass;
- if (fabs(vertices[i].vel.z) > MAX_VEL)
- vertices[i].vel.z = vertices[i].vel.z / fabs(vertices[i].vel.z) * MAX_VEL;
- }
- return self;
- }
-
- // Just erase ourself.
- - drawSelf:(const NXRect *)rects :(int)rectCount
- {
- if (!rects || !rectCount)
- return self;
-
- PSsetlinewidth(0);
- PSsetgray(0);
- NXRectFill(rects);
-
- return self;
- }
-
- // Somebody just changed the size of the box we're sitting in - recompute
- // stuff, and start the animation again.
- - frameChanged:(const NXRect *)frameRect
- {
- D3_PT initPos, initVel;
- int i, j;
- float length;
-
- [self useNewFrame:frameRect];
-
- // Compute the room size.
- backTopRight.x = frameRect->size.width;
- backTopRight.y = frameRect->size.height;
- backTopRight.z = frameRect->size.height;
-
- // Where the perspective's coming from.
- perspectivePt.x = backTopRight.x / 2;
- perspectivePt.y = backTopRight.y / 2;
- perspectivePt.z = backTopRight.y * DEPTH;
-
- // Compute initial velocity.
- initVel.x = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- initVel.y = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- initVel.z = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
-
- // If the room's too small, we're going to have problems, so don't
- // stick the polyhedron in.
- if ((frameRect->size.width < 240) || (frameRect->size.height < 240))
- noAnimation = YES;
- else
- {
- noAnimation = NO;
-
- // Compute initial position.
- initPos.x = randBetween(120, frameRect->size.width - 120);
- initPos.y = randBetween(120, frameRect->size.height - 120);
- initPos.z = randBetween(120, frameRect->size.height - 120);
-
- // Compute the rest lengths of the springs.
- length = distance(offsets[polyhedron][0].x - offsets[polyhedron][1].x, offsets[polyhedron][0].y - offsets[polyhedron][1].y, offsets[polyhedron][0].z - offsets[polyhedron][1].z);
- for (i = 0; i < numVertices; i++)
- {
- vertices[i].pos.x = initPos.x + offsets[polyhedron][i].x * SPRING_REST_LEN / length;
- vertices[i].pos.y = initPos.y + offsets[polyhedron][i].y * SPRING_REST_LEN / length;
- vertices[i].pos.z = initPos.z + offsets[polyhedron][i].z * SPRING_REST_LEN / length;
- vertices[i].vel = initVel;
- }
- for (i = 0; i < numVertices; i++)
- for (j = 0; j < numVertices; j++)
- {
- length = distance(vertices[i].pos.x - vertices[j].pos.x, vertices[i].pos.y - vertices[j].pos.y,
- vertices[i].pos.z - vertices[j].pos.z);
- restLengths[i][j] = length;
- }
- }
-
- // Compute the damping factor
-
- damping = DAMPING * realAdjacents / numVertices;
-
- // sanity check: if this number is too big, then velocity
- // damping contributes to instability, rather than curing it...
-
- if (damping > 0.3)
- damping = 0.3;
-
- return self;
- }
-
-
- // If we get either setFrame, or sizeTo messages, we'd better recompute the
- // box and stuff.
- - setFrame:(const NXRect *)frameRect
- {
- [super setFrame:frameRect];
-
- [self frameChanged: frameRect];
-
- return self;
- }
-
- - sizeTo:(NXCoord)width :(NXCoord)height
- {
- NXRect frameRect;
-
- [super sizeTo:width:height];
-
- frameRect.origin.x = 0;
- frameRect.origin.y = 0;
-
- frameRect.size.width = width;
- frameRect.size.height = height;
-
- [self frameChanged: &frameRect];
-
- return self;
- }
-
- - initFrame:(const NXRect *)frameRect
- {
- [super initFrame: frameRect];
-
- [self useNewFrame:frameRect];
-
- if (frameRect != NULL)
- [self setFrame:frameRect];
-
- return self;
- }
-
- - useNewFrame:(const NXRect *)frameRect
- {
- int i, j, k;
- D3_PT initVel;
- BOOL foundVertex;
-
- // Decide which Polyhedron.
- if (selectedIndex == 0) polyhedron = random() % 5;
- else polyhedron = selectedIndex-1;
-
- numVertices = theNumVertices[polyhedron];
- numAdjacents = theNumAdjacents[polyhedron];
- numFaces = theNumFaces[polyhedron];
- numDrawFaces = realFaces[polyhedron];
- verticesPerFace = theVerticesPerFace[polyhedron];
-
- // Compute adjacency info.
- // Notice that for the purposes of velocity damping, adjacent
- // is the same as "are vertices on the same face." Not "are
- // vertices on the same edge."
- for (i = 0; i < numVertices; i++)
- {
- for (j = 0; j < numVertices; j++)
- isAdjacent[i][j] = NO;
- for (j = 0; j < numFaces; j++)
- {
- foundVertex = NO;
- for (k = 0; k < verticesPerFace; k++)
- if (faces[polyhedron][j][k] == i)
- foundVertex = YES;
- if (foundVertex)
- for (k = 0; k < verticesPerFace; k++)
- if (faces[polyhedron][j][k] != i)
- isAdjacent[i][faces[polyhedron][j][k]] = YES;
- }
- }
-
- realAdjacents = 0;
- for (i = 0; i < numVertices; i++)
- if (isAdjacent[0][i])
- realAdjacents ++;
-
- backTopRight.x = 0;
- backTopRight.y = 0;
- backTopRight.z = 0;
-
- perspectivePt.x = 0;
- perspectivePt.y = 0;
- perspectivePt.z = 0;
-
- noAnimation = YES;
- initVel.x = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- initVel.y = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- initVel.z = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
-
- // set up the initial position of the icosahedron.
-
- for (i = 0; i < numVertices; i++)
- {
- vertices[i].mass = MASS;
- vertices[i].vel = initVel;
- }
-
- backStep = 0;
-
- damping = DAMPING;
-
- return self;
- }
-
- - setSelectedIndex:sender
- {
- int val;
- val = [sender selectedRow];
- if (selectedIndex == val) return self;
-
- selectedIndex = val;
- [self frameChanged: &bounds];
- [self display];
- return self;
- }
-
- - kickIt:sender
- {
- int i;
- float x,y,z;
-
- x = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- y = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
- z = randBetween(-INIT_VELOCITY, INIT_VELOCITY);
-
- for (i = 0; i < numVertices; i++)
- {
- vertices[i].vel.x += x;
- vertices[i].vel.y += y;
- vertices[i].vel.z += z;
- }
- return self;
- }
-
- - inspector:sender
- {
- char buf[MAXPATHLEN];
-
- if (!inspectorPanel)
- {
- sprintf(buf,"%s/Polyhedra.nib",[sender moduleDirectory:"Polyhedra"]);
- [NXApp loadNibFile:buf owner:self withNames:NO];
- }
- return inspectorPanel;
- }
-
- - (BOOL) useBufferedWindow
- { return YES;
- }
-
-
-
- @end
-